#include "BSException/BSException.h"
#include "boost/date_time/gregorian/gregorian.hpp"
#include "DateTime.h"

using namespace SystemTool;
using namespace SystemTool::Clock;

// Construct a date time
DateTime::DateTime(unsigned int iYear,
	unsigned int iMonth,
	unsigned int iDay,
	unsigned int iHour,
	unsigned int iMin,
	unsigned int iSeconds,
	unsigned int iMilliseconds):
	m_iYear(iYear),
	m_iMonth(iMonth),
	m_iDay(iDay),
	m_iHour(iHour),
	m_iMinute(iMin),
	m_iSeconds(iSeconds),
	m_iMilliseconds(iMilliseconds),
	m_bDisposed(false)
{

}

// Destory the date time
DateTime::~DateTime()
{

}

// Copy the date time
DateTime::DateTime(const DateTime& other)
{
	SetYear(other.GetYear());

	SetMonth(other.GetMonth());

	SetDay(other.GetDay());

	SetHour(other.GetHour());

	SetMinute(other.GetMinute());

	SetSeconds(other.GetSeconds());

	SetMilliseconds(other.GetMilliseconds());

	SetDisposed(GetDisposed());
}

// Assign the date time
DateTime& DateTime::operator=(const DateTime& other)
{
	if (this != &other)
	{
		SetYear(other.GetYear());

		SetMonth(other.GetMonth());

		SetDay(other.GetDay());

		SetHour(other.GetHour());

		SetMinute(other.GetMinute());

		SetSeconds(other.GetSeconds());

		SetMilliseconds(other.GetMilliseconds());

		SetDisposed(GetDisposed());
	}

	return *this;
}

// Is two date time equaled
bool DateTime::operator==(const DateTime& other)
{
	if (GetYear() != other.GetYear())
	{
		return false;
	}

	if (GetMonth() != other.GetMonth())
	{
		return false;
	}

	if (GetDay() != other.GetDay())
	{
		return false;
	}

	if (GetHour() != other.GetHour())
	{
		return false;
	}

	if (GetMinute() != other.GetMinute())
	{
		return false;
	}

	if (GetMilliseconds() != other.GetMilliseconds())
	{
		return false;
	}

	return true;
}

// PTime to datetime
DateTime DateTime::ToDateTime(boost::posix_time::ptime& t)
{
	boost::gregorian::date CurDate = t.date();

	boost::posix_time::time_duration CurrentTime = t.time_of_day();

	unsigned long iYear = static_cast<unsigned long>(CurDate.year());

	unsigned long iMonth = static_cast<unsigned long>(CurDate.month());

	unsigned long iDay = static_cast<unsigned long>(CurDate.day());

	unsigned long iHour = static_cast<unsigned long>(CurrentTime.hours());

	unsigned long iMin = static_cast<unsigned long>(CurrentTime.minutes());

	unsigned long iSeconds = static_cast<unsigned long>(CurrentTime.seconds());

	unsigned long iTotalMilliseconds = static_cast<unsigned long>(CurrentTime.total_milliseconds());

	unsigned long iMilliseconds = iTotalMilliseconds - 1000 * (iHour * 3600 + iMin * 60 + iSeconds);

	DateTime MyDateTime(iYear,
		iMonth,
		iDay,
		iHour,
		iMin,
		iSeconds,
		iMilliseconds);

	return MyDateTime;
}

// Datetime to ptime
boost::posix_time::ptime DateTime::ToBoostTime(DateTime& t)
{
	// Create year,month and day
	boost::gregorian::date CurDate(t.GetYear(), t.GetMonth(), t.GetDay());

	// Create hour,minute,seconds,milliseconds
	boost::posix_time::time_duration CurrentTime;

	boost::posix_time::hours h(t.GetHour());

	boost::posix_time::minutes m(t.GetMinute());

	boost::posix_time::seconds s(t.GetSeconds());

	boost::posix_time::milliseconds ms(t.GetMilliseconds());

	CurrentTime = h + m + s + ms;

	boost::posix_time::ptime FinalTime(CurDate, CurrentTime);

	return FinalTime;
}

// Get current date time
DateTime DateTime::Now(DateRegion eDateRegion)
{
	boost::posix_time::ptime CurTime;

	if (eDateRegion == DATE_REGION_LOCAL)
	{
		CurTime = boost::posix_time::second_clock::local_time();
	}
	else
	{
		CurTime = boost::posix_time::second_clock::universal_time();
	}

	return ToDateTime(CurTime);
}

// Convert the date time to time_t
time_t DateTime::ToTime_t()
{
	boost::posix_time::ptime t = ToBoostTime(*this);

	struct tm TmValue = boost::posix_time::to_tm(t);

	time_t FinalTime_t = std::mktime(&TmValue);

	return FinalTime_t;
}

// To string
std::string DateTime::ToString(DateRegion eDateRegion)
{
	boost::posix_time::ptime ptt = ToBoostTime(*this);

	if (eDateRegion == DATE_REGION_LOCAL)
	{
		return boost::posix_time::to_simple_string(ptt);
	}

	return boost::posix_time::to_iso_string(ptt);
}

// Get the date time from the time_t
DateTime DateTime::FromTime_t(time_t TimeDuration)
{
	struct tm* pTmValue = NULL;

	localtime_s(pTmValue, &TimeDuration);

	if (pTmValue == NULL)
	{
		return DateTime(1970,1,1);
	}

	boost::posix_time::ptime pttm = boost::posix_time::ptime_from_tm(*pTmValue);

	return ToDateTime(pttm);
}

// Get the date time from the string
DateTime DateTime::FromString(std::string strTime, DateRegion eDateRegion)
{
	if (strTime.empty())
	{
		return DateTime();
	}

	boost::posix_time::ptime ptt;

	try
	{
		try
		{
			if (eDateRegion == DATE_REGION_LOCAL)
			{
				ptt = boost::posix_time::time_from_string(strTime);
			}
			else
			{
				ptt = boost::posix_time::from_iso_string(strTime);
			}
		}
		catch (boost::bad_lexical_cast& e)
		{
			std::string strErrorMsg = std::string("Bad time format,maybe you should input time as local: yyyy-MM-dd hh:mm:ss \
				or as iso: yyyyMMddThhmmss ") + e.what();

			throw BSException() << strErrorMsg;
		}
	}
	catch (BSException& e)
	{
		throw e;
	}

	return ToDateTime(ptt);
}
